/*
 *  blob - generic type for storing an array and a length.
 *
 *  This file initially created by Google, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <connman/blob.h>
#include <glib.h>
#include <errno.h>
#include <string.h>

/*
 * Allocate memory for a new blob structure and its internal buffer.
 *
 * If data is non-NULL, the first len bytes of it will be used to initialize
 * the new blob's internal buffer.  The internal buffer is automatically freed
 * by blob_free().
 *
 * If data is NULL, it can be assigned by the caller to point to any buffer.
 * That buffer will not be freed by blob_free().
 *
 * @blob: pointer to pointer to the new blob
 * @len:  length of new blob's data array
 * @data: if not NULL, used to initialize new blob's data array.
 * @return: -ENOMEM, or 0 on success.
 */
int blob_new(struct blob **blob, size_t len, const char *data)
{
	/* Allocate struct followed by an internal buffer. */
	*blob = g_try_malloc(sizeof(**blob) + len);
	if (*blob == NULL)
		return -ENOMEM;

	(*blob)->len = len;
	if (len == 0) {
		/*
		 * When len ==0, there is no internal buffer.
		 * Instead point to caller's data, usually NULL.
		 * If not NULL, caller must manually set len, and free data.
		 */
		(*blob)->data = (char *)data;
	} else {
		/* Point to the internal buffer. */
		(*blob)->data = (*blob)->buffer;

		/* If data was provided, use it to initialize buffer */
		if (data != NULL)
			memcpy((*blob)->data, data, len);
	}

	return 0;
}

/*
 * Allocate memory for a new blob structure, and initialize its buffer from the
 * given '\0'-terminated string of ASCII hexadecimal digits.
 *
 * @blob: pointer to pointer to the new blob
 * @hex:  '\0'-terminated string whose length must be > 0,
 *        and must have an even number of digits.
 * @return: -ENOMEM, -EINVAL or 0 on success.
 */
int blob_new_from_hex(struct blob **blob, const char *hex)
{
	size_t hex_len;
	size_t blob_len;
	int ret;
	int i;

	for (i = 0; hex[i] != '\0'; i++)
		if (!g_ascii_isxdigit(hex[i]))
			return -EINVAL;

	hex_len = i;
	if (hex_len == 0 || (hex_len % 2) != 0)
		return -EINVAL;

	blob_len = hex_len / 2;
	ret = blob_new(blob, blob_len, NULL);
	if (ret < 0)
		return ret;

	for (i = 0; i < blob_len; i++)
		(*blob)->data[i] = 16 * g_ascii_xdigit_value(hex[2*i])
				+ g_ascii_xdigit_value(hex[2*i+1]);

	return 0;
}

/*
 * Free memory for blob structure and  its internal buffer.
 * @blob: blob to free
 */
void blob_free(struct blob *blob)
{
	g_free(blob);
}

